﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Halfwit.ViewModels;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using TweetSharp.Twitter.Model;
using System.Windows.Automation;
using System.Reflection;

namespace Halfwit.Views
{
    /// <summary>
    /// Interaction logic for HomePage.xaml
    /// </summary>
    public partial class TweetsPage : Page, IDeferable, ICanHasTextSelection
    {
        public TweetsPage(TweetsViewModel viewModel)
        {
            InitializeComponent();

            tweetList.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
            writeButton.Checked += writeButton_Checked;
            writeButton.Unchecked += writeButton_Unchecked;
            updateText.KeyDown += updateText_KeyDown;
            tweetList.PreviewKeyDown += tweetList_KeyDown;
            tweetList.SelectionChanged += tweetList_SelectionChanged;

            _model = viewModel;
            this.DataContext = _model;

            foreach (var ib in _model.InputBindings)
            {
                this.InputBindings.Add(ib);
            }

            this.Loaded += new RoutedEventHandler(TweetsPage_Loaded);
            this.Unloaded += new RoutedEventHandler(TweetsPage_Unloaded);
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            if (e.KeyboardDevice.Modifiers != ModifierKeys.Control) return;

            if (e.Key == Key.W)
            {
                e.Handled = true;
                writeButton.IsChecked = true;
                // make sure the textbox is focused in case writeButton was already checked
                Dispatcher.BeginInvoke(
                    new Action(() => FocusUpdateText()),
                    System.Windows.Threading.DispatcherPriority.Background);

                return;
            }
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            tweetList.Focus();
        }

        void TweetsPage_Loaded(object sender, RoutedEventArgs e)
        {
            // don't bother refreshing if we already have tweets?
            //if (!_view.View.IsEmpty) return;

            Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
            {
                _model.Refresh.Execute(null);
            }));
        }


        void TweetsPage_Unloaded(object sender, RoutedEventArgs e)
        {
            _model.IsActive = false;
        }

        TweetsViewModel _model;

        private void FocusUpdateText()
        {
            updateText.Focus();
            updateText.SelectionStart = updateText.Text.Length;
        }

        void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (tweetList.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;

            tweetList.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
            FocusTweetList();
        }

        private void FocusTweetList()
        {
            tweetList.Focus();
            if (tweetList.Items.Count == 0) return;

            var index = tweetList.SelectedIndex;
            if (index < 0) return;

            var item = tweetList.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
            if (item != null)
            {
                item.Focus();
                return;
            }

            Action action = () =>
                {
                    tweetList.ScrollIntoView(tweetList.SelectedItem);

                    item = tweetList.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
                    if (item == null) return;

                    item.Focus();
                };

            Dispatcher.BeginInvoke(DispatcherPriority.Background, action);
        }

        private void tweetList_KeyDown(object sender, KeyEventArgs e)
        {
            var lb = sender as ListBox;
            if (lb == null) return;

            var item = lb.SelectedItem as TwitterStatus;
            if (item == null) return;

            if (e.Key == Key.Back)
            {
                e.Handled = true;
                if (_model.FindSource.CanExecute(item))
                {
                    _model.FindSource.Execute(item);
                }
                return;
            }

            // they have to have either control and optionally shift down to make it past here
            if (e.KeyboardDevice.Modifiers != ModifierKeys.Control &&
                e.KeyboardDevice.Modifiers != (ModifierKeys.Control | ModifierKeys.Shift)
                ) return;

            if (e.Key == Key.R)
            {
                e.Handled = true;
                if (_model.Reply.CanExecute(item))
                {
                    _model.Reply.Execute(item);
                    // make sure the textbox is focused in case writeButton was already checked
                    Dispatcher.BeginInvoke(
                        new Action(() => FocusUpdateText()),
                        System.Windows.Threading.DispatcherPriority.Background);
                }
                return;
            }

            // if anything other than control is down, drop out here
            if (e.KeyboardDevice.Modifiers != ModifierKeys.Control) return;

            if (e.Key == Key.C)
            {
                e.Handled = true;
                if (_model.Copy.CanExecute(item))
                {
                    _model.Copy.Execute(item);
                }
                return;
            }

            if (e.Key == Key.T)
            {
                e.Handled = true;
                if (_model.Retweet.CanExecute(item))
                {
                    _model.Retweet.Execute(item);
                    // make sure the textbox is focused in case writeButton was already checked
                    Dispatcher.BeginInvoke(
                        new Action(() => FocusUpdateText()),
                        System.Windows.Threading.DispatcherPriority.Background);
                }
                return;
            }

            if (e.Key == Key.F)
            {
                e.Handled = true;
                if (_model.Favourite.CanExecute(item))
                {
                    _model.Favourite.Execute(item);
                }
                return;
            }

            if (e.Key == Key.Delete)
            {
                e.Handled = true;
                if (_model.Delete.CanExecute(item))
                {
                    _model.Delete.Execute(item);
                }
                return;
            }

            if (e.Key == Key.L)
            {
                e.Handled = true;
                if (_model.Follow.CanExecute(item.User))
                {
                    _model.Follow.Execute(item.User);
                }
                return;
            }

            if (e.Key == Key.D)
            {
                e.Handled = true;
                // make sure the textbox is focused in case writeButton was already checked
                Dispatcher.BeginInvoke(
                    new Action(() => FocusUpdateText()),
                    System.Windows.Threading.DispatcherPriority.Background);

                if (_model.DirectMessage.CanExecute(item.User))
                {
                    _model.DirectMessage.Execute(item.User);
                }
                return;
            }

            if (e.Key == Key.U)
            {
                e.Handled = true;
                if (_model.NavigateToUser.CanExecute(item.User.ScreenName))
                {
                    _model.NavigateToUser.Execute(item.User.ScreenName);
                }
                return;
            }
        }

        private void writeButton_Checked(object sender, RoutedEventArgs e)
        {
            Dispatcher.BeginInvoke(
                System.Windows.Threading.DispatcherPriority.Background,
                new Action(() => FocusUpdateText()));
        }

        private void writeButton_Unchecked(object sender, RoutedEventArgs e)
        {
            Dispatcher.BeginInvoke(
                System.Windows.Threading.DispatcherPriority.Background,
                new Action(() => FocusTweetList()));
        }

        private void updateText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                e.Handled = true;
                _model.IsUpdating = false;
                return;
            }

            if (e.Key == Key.Return)
            {
                e.Handled = true;
                if (_model.Tweet.CanExecute(null))
                {
                    _model.Tweet.Execute(null);
                }
                return;
            }

            if (e.Key == Key.Tab && CaretIndex == SelectionStart && SelectionLength > 0)
            {
                e.Handled = true;

                // deselect any text and jump the caret over it
                CaretIndex = SelectionStart + SelectionLength;
                SelectionLength = 0;
            }
        }

        #region IDeferable Members and Helpers

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            _view = this.Resources["TweetsView"] as CollectionViewSource;
        }

        private CollectionViewSource _view;

        public IDisposable DeferRefresh()
        {
            if (_view == null) return null;
            return _view.DeferRefresh();
        }

        #endregion

        private void tweetList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Dispatcher.BeginInvoke(
                System.Windows.Threading.DispatcherPriority.ApplicationIdle,
                new Action(() => FocusTweetList()));
        }

        private void updateText_SelectionChanged(object sender, RoutedEventArgs e)
        {
            _model.UpdateTextSelectionStart = updateText.SelectionStart;
            _model.UpdateTextSelectionLength = updateText.SelectionLength;
        }

        public void SetSelection(int start, int length)
        {
            updateText.SelectionStart = start;
            updateText.SelectionLength = length;
        }

        public string SelectedText
        {
            get
            {
                return updateText.SelectedText;
            }
            set
            {
                updateText.SelectedText = value;
            }
        }

        public int CaretIndex
        {
            get
            {
                return updateText.CaretIndex;
            }
            set
            {
                updateText.CaretIndex = value;
            }
        }

        public int SelectionStart
        {
            get
            {
                return updateText.SelectionStart;
            }
            set
            {
                updateText.SelectionStart = value;
            }
        }

        public int SelectionLength
        {
            get
            {
                return updateText.SelectionLength;
            }
            set
            {
                updateText.SelectionLength = value;
            }
        }
    }

    public interface IDeferable
    {
        IDisposable DeferRefresh();
    }

    // yes, this is a nasty hack so I can mess with the TextBox from the ViewModel
    public interface ICanHasTextSelection
    {
        string SelectedText { get; set; }
        int CaretIndex { get; set; }
        int SelectionStart { get; set; }
        int SelectionLength { get; set; }
    }
}



